package cn.edu.dlmu.cogo.framework.web;

import cn.edu.dlmu.cogo.framework.annotation.RequestParam;
import cn.edu.dlmu.cogo.framework.context.ContextContainer;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.net.multipart.UploadFile;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.http.server.HttpServerRequest;
import cn.hutool.http.server.HttpServerResponse;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author yixi
 */
public class ParamBinder {

    private final Class<?> clazz;

    private final Method method;

    private final HttpServerRequest request;

    private final HttpServerResponse response;

    public ParamBinder(Class<?> clazz, Method method, HttpServerRequest request, HttpServerResponse response){
        this.clazz = clazz;
        this.method = method;
        this.request = request;
        this.response = response;
    }
    /**
     * 获取方法的参数名称
     *
     * @return 参数名称集合
     */
    private List<String> getParameterNames() {
        List<String> parameterNames = new ArrayList<>();
        for (Parameter parameter : method.getParameters()) {
            RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
            if (requestParam == null) {
                parameterNames.add("");
            } else {
                parameterNames.add(requestParam.value());
            }
        }
        return parameterNames;
    }

    public Object invoke() throws InvocationTargetException, IllegalAccessException {
        Object instance = ContextContainer.getApplicationContext().getBean(clazz);
        Parameter[] parameters = method.getParameters();
        List<String> parameterNames = getParameterNames();
        List<Object> parameterValues = new ArrayList<>();
        if (parameters != null && parameters.length > 0) {
            for (int i = 0; i < parameters.length; i++) {
                if (parameters[i].getType() == HttpServerRequest.class) {
                    parameterValues.add(request);
                } else if (parameters[i].getType() == HttpServerResponse.class) {
                    parameterValues.add(response);
                } else if (parameters[i].getType() == Integer.class || "int".equals(parameters[i].getType().getName())) {
                    parameterValues.add(Convert.toInt(request.getParam(parameterNames.get(i))));
                } else if (parameters[i].getType() == Double.class || "double".equals(parameters[i].getType().getName())) {
                    parameterValues.add(Convert.toDouble(request.getParam(parameterNames.get(i))));
                } else if (parameters[i].getType() == Long.class || "long".equals(parameters[i].getType().getName())) {
                    parameterValues.add(Convert.toLong(request.getParam(parameterNames.get(i))));
                } else if (parameters[i].getType() == Boolean.class || "boolean".equals(parameters[i].getType().getName())) {
                    parameterValues.add(Convert.toBool(request.getParam(parameterNames.get(i))));
                } else if (parameters[i].getType() == String.class) {
                    parameterValues.add(request.getParam(parameterNames.get(i)));
                } else if (parameters[i].getType() == UploadFile.class) {
                    parameterValues.add(request.getMultipart().getFile(parameterNames.get(i)));
                } else {
                    Class<?> aClass = parameters[i].getType();
                    parameterValues.add(getBean(aClass, request));
                }
            }
        }
        return method.invoke(instance, parameterValues.toArray());
    }


    public Object getBean(Class<?> tClass, HttpServerRequest request) {
        try {
            Object instance = ReflectUtil.newInstance(tClass);
            Field[] fields = ReflectUtil.getFields(tClass);
            for (Field field : fields) {
                String parameter = request.getParam(field.getName());
                if (null != parameter) {
                    Method setMethod;
                    if (!field.getName().startsWith("is")) {
                        setMethod = ReflectUtil.getMethodByNameIgnoreCase(tClass, "set" + field.getName());
                    } else {
                        setMethod = ReflectUtil.getMethodByNameIgnoreCase(tClass, "set" + field.getName().substring(2));
                    }
                    String parameterType = setMethod.getParameterTypes()[0].getName();
                    switch (parameterType) {
                        case "int":
                        case "java.lang.Integer":
                            setMethod.invoke(instance, Integer.parseInt(parameter));
                            break;
                        case "double":
                        case "java.lang.Double":
                            setMethod.invoke(instance, Double.parseDouble(parameter));
                            break;
                        case "float":
                        case "java.lang.Float":
                            setMethod.invoke(instance, Float.parseFloat(parameter));
                            break;
                        case "boolean":
                        case "java.lang.Boolean":
                            setMethod.invoke(instance, Boolean.parseBoolean(parameter));
                            break;
                        case "long":
                        case "java.lang.Long":
                            setMethod.invoke(instance, Long.parseLong(parameter));
                            break;
                        default:
                            setMethod.invoke(instance, parameter);
                            break;
                    }
                }
            }
            return instance;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
